package com.filex.ads.common;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.os.AsyncTask;
import android.os.IBinder;
import android.os.IInterface;
import android.os.Looper;
import android.os.Parcel;
import android.os.RemoteException;
import android.support.annotation.Nullable;


import com.filex.ads.common.logging.Logger;
import com.filex.ads.common.utils.AsyncTasks;

import java.util.concurrent.LinkedBlockingQueue;



public class GpsHelper {

    static public final String IS_LIMIT_AD_TRACKING_ENABLED_KEY = "isLimitAdTrackingEnabled";
    private static final String ADVERTISING_INTERFACE_NAME = "com.google.android.gms.ads.identifier.internal.IAdvertisingIdService";

    public static class AdvertisingInfo {
        public final String advertisingId;
        public final boolean limitAdTracking;

        public AdvertisingInfo(String adId, boolean limitAdTrackingEnabled) {
            advertisingId = adId;
            limitAdTracking = limitAdTrackingEnabled;
        }
    }

    public interface GpsHelperListener {
        void onFetchAdInfoCompleted(AdvertisingInfo advertisingInfo);
        void onFetchAdInfoFailed(String info);
    }


    public static boolean isLimitAdTrackingEnabled(Context context) {
        return FilexSPHelper.getSharedPreferences(context)
                .getBoolean(IS_LIMIT_AD_TRACKING_ENABLED_KEY, false); // default to disabled
    }

    public static void fetchAdvertisingInfoAsync(final Context context, final GpsHelperListener gpsHelperListener) {
        internalFetchAdvertisingInfoAsync(context, gpsHelperListener);
    }

    @Nullable
    static public AdvertisingInfo fetchAdvertisingInfoSync(final Context context) throws  Exception {
        if (Looper.myLooper() == Looper.getMainLooper())
            throw new IllegalStateException("Cannot be called from the main thread");

        try {
            PackageManager pm = context.getPackageManager();
            pm.getPackageInfo("com.android.vending", 0);
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }

        AdvertisingConnection connection = new AdvertisingConnection();
        Intent intent = new Intent("com.google.android.gms.ads.identifier.service.START");
        intent.setPackage("com.google.android.gms");

        if (context.bindService(intent, connection, Context.BIND_AUTO_CREATE)) {
            try {
                AdvertisingInterface adInterface = new AdvertisingInterface(connection.getBinder());
                AdvertisingInfo adInfo = new AdvertisingInfo(adInterface.getId(),
                        adInterface.isLimitAdTrackingEnabled(true));
                return adInfo;
            } catch (Exception exception) {
                throw exception;
            } finally {
                context.unbindService(connection);
            }
        }
        throw new IllegalStateException("");
    }


    private static final class AdvertisingConnection implements ServiceConnection {

        boolean mRetrieved = false;

        private LinkedBlockingQueue<IBinder> mQueue = new LinkedBlockingQueue<>(1);

        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            try {
                mQueue.put(iBinder);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {

        }

        public IBinder getBinder()  throws InterruptedException  {

            if (mRetrieved) {
                throw new IllegalStateException("Binder hash retrieved");
            }
            mRetrieved = true;
            return mQueue.take();
        }
    }

    private static final class AdvertisingInterface implements IInterface {


        private IBinder mBinder;

        public AdvertisingInterface(IBinder binder ) {
            this.mBinder = binder;
        }

        @Override
        public IBinder asBinder() {
            return mBinder;
        }

        public String getId() throws RemoteException {
            Parcel data = Parcel.obtain();
            Parcel reply = Parcel.obtain();
            String id;
            try {
                data.writeInterfaceToken(ADVERTISING_INTERFACE_NAME);
                mBinder.transact(1, data, reply, 0);
                reply.readException();
                id = reply.readString();
            } finally {
                reply.recycle();
                data.recycle();
            }
            return id;
        }

        public boolean isLimitAdTrackingEnabled(boolean paramBoolean) throws RemoteException {
            Parcel data = Parcel.obtain();
            Parcel reply = Parcel.obtain();
            boolean limitAdTracking;
            try {
                data.writeInterfaceToken("com.google.android.gms.ads.identifier.internal.IAdvertisingIdService");
                data.writeInt(paramBoolean ? 1 : 0);
                mBinder.transact(2, data, reply, 0);
                reply.readException();
                limitAdTracking = 0 != reply.readInt();
            } finally {
                reply.recycle();
                data.recycle();
            }
            return limitAdTracking;
        }
    }

    static private void internalFetchAdvertisingInfoAsync(final Context context, final GpsHelperListener gpsHelperListener) {
        try {
            AsyncTasks.safeExecuteOnExecutor(new FetchAdvertisingInfoTask(context, gpsHelperListener));
        } catch (Exception exception) {
            Logger.d("Error executing FetchAdvertisingInfoTask", exception);

            if (gpsHelperListener != null) {
                gpsHelperListener.onFetchAdInfoFailed(exception.getMessage());
            }
        }
    }

    static private class FetchAdvertisingInfoTask extends AsyncTask<Void, Void, AdvertisingInfo> {
        private Context mContext;
        private GpsHelperListener mGpsHelperListener;
        public FetchAdvertisingInfoTask(Context context, GpsHelperListener gpsHelperListener) {
            mContext = context.getApplicationContext();
            mGpsHelperListener = gpsHelperListener;
        }

        @Override
        protected AdvertisingInfo doInBackground(Void... voids) {
            try {
                if (mContext == null) {
                    return null;
                }
                AdvertisingInfo adInfo = fetchAdvertisingInfoSync(mContext);
                if (adInfo != null) {
                    // updateClientMetadata(context, adInfo);
                    FilexSPHelper.setString(mContext, "gaid", adInfo.advertisingId);
                }
                return adInfo;
            } catch (Exception exception) {
                Logger.d("Unable to obtain Google AdvertisingIdClient.Info via reflection.");
            }

            return null;
        }

        @Override
        protected void onPostExecute(AdvertisingInfo advertisingInfo) {
            GpsHelperListener gpsHelperListener = mGpsHelperListener;
            if (gpsHelperListener != null) {
                if (advertisingInfo != null) {
                    gpsHelperListener.onFetchAdInfoCompleted(advertisingInfo);
                } else {
                    gpsHelperListener.onFetchAdInfoFailed("Unable to obtain Google AdvertisingIdClient.Info via reflection.");
                }
            }
        }
    }
}

